package stu

import (
	"context"
	"fmt"
	"log"
	"math"
	"strconv"
	"stu.stdx.com/src/login"
	"sync"
	"time"
)

type TaskQueue struct {
	taskList     []VideoTask   // 任务列表
	tickerTime   time.Duration // 轮询间隔时间
	WebSocketURL string
}

// --------------------------
// 构造函数
// --------------------------
func NewTaskQueue() *TaskQueue {
	return &TaskQueue{
		taskList:   make([]VideoTask, 0),
		tickerTime: 2,
	}
}

// --------------------------
// 队列操作方法
// --------------------------
func (q *TaskQueue) SetWaitTime(waitTime time.Duration) {
	q.tickerTime = waitTime
}

// AddTasks 会将多个任务添加到任务队列中

func (q *TaskQueue) AddTasks(tasks []VideoTask) {
	q.taskList = append(q.taskList, tasks...)
}

// ExecuteTasks 会执行任务队列中的所有任务
func (q *TaskQueue) ExecuteTasks() {
	for _, t := range q.taskList {
		// 执行任务（同步）
		if err := q.executeTaskSync(&t); err != nil {
			log.Printf("[TASK-ERROR] ID: %s, Err: %v", t.Jjwikiid, err)
			continue // 如果出错，跳过当前任务，继续下一个
		}
	}
}
func (q *TaskQueue) executeTaskSync(task *VideoTask) error {
	log.Printf("[TASK-START] ID: %s", task.Jjwikiid)

	// 读取 cookies
	phpsessid, err := login.GetPHPSESSID()

	// 创建 WebSocket 连接
	mainAddr := fmt.Sprintf("%s?session_id=%s", task.WebSocketURL, phpsessid)
	wsConn, err := NewWSConnection(mainAddr)
	if err != nil {
		log.Printf("[WS-ERROR] ID: %s, Err: %v", task.Jjwikiid, err)
		return fmt.Errorf("failed to create WebSocket connection: %v", err)
	}
	defer wsConn.Close()

	// 创建 WebSocket ping 连接
	pingAddr := fmt.Sprintf("%s?session_id=%s", task.WebSocketPing, phpsessid)
	pingConn, err := NewWSConnection(pingAddr)
	if err != nil {
		log.Printf("[PING-WS-ERROR] ID: %s, Err: %v", task.Jjwikiid, err)
		return fmt.Errorf("failed to create WebSocket ping connection: %v", err)
	}
	defer pingConn.Close()
	fmt.Printf("\n[PROGRESS] %s\n", task.formatProgress())
	if q.tickerTime == 0 {
		log.Printf("[TASK-ERROR] ID: %s, Err: 轮询间隔时间不能为0", task.Jjwikiid)
		return fmt.Errorf("轮询间隔时间不能为0")
	}

	// 使用 context 来控制任务执行
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// 创建一个 ticker 用于定时发送消息

	// 使用 WaitGroup 确保所有 goroutine 完成（虽然在这个设计中可能不需要）
	var wg sync.WaitGroup
	wg.Add(2)
	// 创建定时器

	// 启动一个 goroutine 来处理定时发送
	go func() {
		defer wg.Done()
		ticker := time.NewTicker(2000 * time.Millisecond)
		defer ticker.Stop()
		for {
			select {
			// 定时触发定时器，并发送消息
			case <-ticker.C:

				recv, err := wsConn.SendProgress(task.ToSocketMessageValues())
				if err != nil {
					log.Printf("[RECV-ERROR] ID: %s, Err: %v", task.Jjwikiid, err)
					cancel() // 通知退出
					return
				}

				// 检查是否需要终止任务
				if shouldTerminate(recv) {
					log.Printf("[TERMINATE] ID: %s, Msg: %s", task.Jjwikiid, recv)
					cancel() // 通知退出
					return
				}

				if isLoginExpired(recv) {
					log.Printf("[LOGIN-EXPIRED] ID: %s, Msg: %s", task.Jjwikiid, recv)
					cancel() //  通知退出
					return
				}

				// 更新当前进度
				task.ShowProgressBar(50) // 这里可以加上一个进度条的显示

				// 判断任务是否完成
				if task.CurJindu >= task.TotalLength {
					fmt.Print("\n[COMPLETED] ---------END-------\n")
					cancel() // 通知退出
					return
				}
				// 更新当前进度
				MaxCurJindu(recv, task)

			case <-ctx.Done():
				return // 退出 goroutine
			}
		}
	}()
	go pingConn.SendHeartbeat(&wg, task.WebSocketPing, ctx)
	wg.Wait() // 确保 goroutine 完成
	return nil
}

// --------------------------
// 工具函数
// --------------------------
func (vt *VideoTask) formatProgress() string {
	output := fmt.Sprintf("---------START-------\n")
	output += fmt.Sprintf("视频名称：%s\n", vt.CourseName)
	output += fmt.Sprintf("视频类型：%s （1:课程录播 2：直播回放）\n", vt.UpdateType)
	output += fmt.Sprintf("视频地址：%s\n", vt.CoursePath)
	output += fmt.Sprintf("当前进度：%d/%d\n", vt.CurJindu, vt.TotalLength)
	output += fmt.Sprintf("视频总时长: %02d:%02d:%02d\n", vt.TotalLength/3600, (vt.TotalLength%3600)/60, vt.TotalLength%60)
	output += fmt.Sprintf("当前视频ID：%v.\n", vt.Jjwikiid)
	return output
}

func shouldTerminate(msg string) bool {
	return msg == "other" || msg == "same"
}
func isLoginExpired(msg string) bool {
	return msg == "<script>top.location.href='login.php';</script>"
}
func MaxCurJindu(recv string, task *VideoTask) {
	recvCurrentTime, err := strconv.Atoi(recv)
	if err != nil {
		return
	}
	if recvCurrentTime >= 888888 {
		task.CurJindu = task.CurJindu + 6
	} else {
		if recvCurrentTime >= 0 && recvCurrentTime >= task.TotalLength {
			task.CurJindu = task.TotalLength + 1
		} else {
			task.CurJindu = int(math.Min(float64(task.TotalLength), float64(recvCurrentTime+6)))
		}
	}

}
